#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <mutex>
using namespace std;

// 设定多个线程进行对临界资源的争夺
#define NUM 5

// 线程什么时候可能切换：(1. 时间片到了; 2.检测时间：从内核态返货用户态的时候)
// 为了保证每个线程之间访问临界资源是互斥的，需要对临界资源加锁

// 加锁后，只实现各个线程之间的访问共享资源是互斥的，但是并没有实现线程之间是同步的
// 因此会出现一个进程一直在进行抢票，其他线程无法抢夺到锁

// 加锁后线程是串行运行，效率低
// 不加锁线程是并行运行，效率高
class Ticket
{
public:
    Ticket()
        : _tickets(1000)
    {
        pthread_mutex_init(&_mtx, nullptr);
    }
    bool GetTicket()
    {
        // 静态/全局定义的锁
        static pthread_mutex_t static_mtx = PTHREAD_MUTEX_INITIALIZER;
        // bool变量是局部变量，因此是每个线程独有的资源，在各自线程栈上进行开辟空间
        bool flag = true;
        /*
            通过加锁，使执行这部分临界资源的线程是互斥的
            锁能被各个线程看见，因此锁也是临界资源
            首先需要保证锁本身是安全的, 系统如何保证锁是安全的呢？
            如果锁实现过程是原子的，那么锁就是安全的
            锁的汇编代码是通过swap/exchange交换CPU寄存器和内存中的数据，当第一个线程抢到锁时，把内存中的数据(1)交换到寄存器中，他就开始执行临界区中的代码
            在执行临界区代码是，线程可能会被切走，因此线程数据要保存到上下文中（包括锁数据，其他线程无法访问临界区）
            其他线程就会被挂起，直到抢到锁的线程归还锁
            PS：但此时存在一个问题————第一次抢到锁的线程可能会一直抢到锁，因此引出 同步 的概念
        */

        pthread_mutex_lock(&_mtx);
        // pthread_mutex_lock(&static_mtx);
        // mtx_.lock();
        if (_tickets > 0)
        {
            usleep(1000);
            cout << "I am new thread " << pthread_self() << ", count = " << _tickets << endl;
            _tickets--;
        }
        else
        {
            cout << "ticket sell empty" << endl;
            flag = false;
        }
        pthread_mutex_unlock(&_mtx);
        // pthread_mutex_unlock(&static_mtx);
        // mtx_.unlock();

        return flag;
    }
    ~Ticket()
    {
        pthread_mutex_destroy(&_mtx);
    }

private:
    int _tickets;
    // 原生线程库，系统层面的互斥锁
    pthread_mutex_t _mtx;
    // C++语言层面的互斥锁
    mutex mtx_;
};

void *thread_run(void *args)
{
    // 加互斥锁
    Ticket *t = (Ticket *)args;
    while (true)
    {
        if (t->GetTicket() == false)
            break;
    }

    // 不加互斥锁
    // int id = *(int *)args;
    // delete (int *)args;
    // while (true)
    // {
    //     if (tickets > 0)
    //     {
    //         usleep(1000);
    //         cout << "I am new thread [" << id << "], count = " << tickets << endl;
    //         tickets--;
    //     }
    //     else
    //     {
    //         break;
    //     }
    // }
}
int main()
{
    pthread_t tid[NUM];
    Ticket *t = new Ticket();

    for (int i = 0; i < NUM; ++i)
    {
        // int *id = new int(i);
        // pthread_create(tid + i, nullptr, thread_run, (void *)id);
        pthread_create(tid + i, nullptr, thread_run, (void *)t);
    }

    for (int i = 0; i < NUM; ++i)
    {
        pthread_join(tid[i], nullptr);
    }
    return 0;
}